
#pragma once

#include <fstream>
#include <vector>
#include <string>
#include <filesystem>

namespace Extension
{

    class FileUtil
    {
    public:
        inline static void FileCopy(std::string_view srcPath, std::string_view destPath, bool overwrite = true);
        inline static std::string ReadAllText(std::string const &filepath);
        inline static std::vector<std::string> ReadAllLines(std::string const &filepath);
        inline static bool Exists(std::string const &filepath);
        inline static void CreateFile(std::string_view filepath, std::string_view content);
    };

    inline void FileUtil::FileCopy(std::string_view srcPath, std::string_view destPath, bool overwrite)
    {
        if (!overwrite && std::filesystem::exists(destPath))
        {
            // file already exists
            return;
        }

        if (overwrite)
        {
            // using stream
            std::ifstream src(srcPath.data(), std::ios::binary);
            if (src)
            {
                std::ofstream dest(destPath.data(), std::ios::trunc | std::ios::binary);
                if (dest)
                {
                    dest << src.rdbuf();
                }
            }
        }
    }

    inline std::string FileUtil::ReadAllText(std::string const &filepath)
    {
        std::ifstream in(filepath, std::ios_base::ate | std::ios_base::binary);
        int filesize = in.tellg();
        in.seekg(0, std::ios_base::beg);
        std::vector<char> buffer(filesize + 1, '\0');
        in.read(&buffer[0], filesize);
        // due to raii , don't need to close fstream.

        return buffer.data();
    };

    inline std::vector<std::string> FileUtil::ReadAllLines(std::string const &filepath)
    {
        std::ifstream in(filepath);
        std::vector<std::string> res{};
        std::string buffer{};
        while (std::getline(in, buffer))
        {
            res.push_back(buffer);
        };

        return res;
    }

    inline bool FileUtil::Exists(std::string const &filepath)
    {
        return std::filesystem::exists(filepath);
    }

    inline void FileUtil::CreateFile(std::string_view filepath, std::string_view content)
    {
        std::ofstream dest(filepath.data());
        if (dest.good())
        {
            dest << content.data();
            dest.flush();
            dest.close();
        }
        else
        {
            throw std::runtime_error(std::string("failed to create file") + filepath.data());
        }
    }
}